2012-09-02 13:56:00
我们先来看一组接口定义:
1struct IX
2{
3 virtual void MethodX() = 0;
4};
5
6struct IXA : public IX
7{
8 virtual void MethodXA() = 0;
9};
10
11struct IXB : public IX
12{
13 virtual void MethodXB() = 0;
14};
15
16struct IXAB : public IXA, public IXB
17{
18 virtual void MethodXAB() = 0;
19};
然后我们来实现 IX:
1struct CX : public IX
2{
3 virtual void MethodX()
4 {
5
6 }
7};
然后再来实现 IXA:
1struct CXA : public IXA
2{
3 virtual void MethodX()
4 {
5
6 }
7
8 virtual void MethodXA()
9 {
10
11 }
12};
这样写当然是没问题的。但是,有木有发现,这里出现MethodX了。如果,我们需要之前CX中对MethodX的实现,那怎么办呢?
一开始我傻傻地想,继承CX不就好了么:
1struct CXA : public IXA, public CX
2{
3 virtual void MethodXA()
4 {
5
6 }
7};
结果实例化CXA的时候报错:
error C2259: 'CXA' : cannot instantiate abstract class
due to following members:
void IX::MethodX(void)' : is abstract
see declaration of 'IX::MethodX'
(嘿嘿。我不知道是不是挺多人一开始都会这么想的。)究其原因,是因为添加多继承不会产生覆盖虚函数的效果。此时虚函数的状态是:
|方法名|直属类/接口|间接类/接口|是否已实现| |---|---|---|---|批odu |MethodX|来自于IXA|来自于IX|未实现| |MethodXA|来自于IXA||CXA实现| |MethodX|来自于CX|来自于IX|CX实现|
新添加的CX中的MethodX,只是覆盖了CX多继承的IX中的那个MethodX。退一步,即便CX是个独立的类,没有继承于IX,CX中的MethoxX也无法为覆盖IXA中的MethodX。只有处于同一继承链中的各个类才会产生覆盖虚函数的效果。
假如,CX就是继承于IXA的,且只实现了MethodX,未实现MethodA:
1struct CX : public IXA
2{
3 virtual void MethodX()
4 {
5
6 }
7};
然后CXA不再继承IXA,而是直接继承CX,补充实现MethodA:
1struct CXA : public CX
2{
3 virtual void MethodXA()
4 {
5
6 }
7};
这样,CXA是可以直接实例化的。但是,CX又无法单独使用了!
CX面临的问题是,需要随时被改变基类,也就是基类类型可配置。于是我们想到了模版:
1template <typename T = IX>
2struct CX : public T
3{
4 virtual void MethodX()
5 {
6
7 }
8};
9
10struct CXA : public CX<IXA>
11{
12 virtual void MethodXA()
13 {
14
15 }
16};
CX单独使用的时候,使用默认模版参数,让它继承于IX;被CXA使用的时候,让它继承于IXA,问题就解决了!同样的方法,我们实现CXB:
1struct CXB : public CX<IXB>
2{
3 virtual void MethodXB()
4 {
5
6 }
7};
下面我们来实现IXAB:
1struct CXAB : public IXAB
2{
3 virtual void MethodX()
4 {
5
6 }
7
8 virtual void MethodXA()
9 {
10
11 }
12
13 virtual void MethodXB()
14 {
15
16 }
17
18 virtual void MethodXAB()
19 {
20
21 }
22};
注意到这里只有一个MethodX。由于MethodX对IXA、IXB是同源的,IXAB里面虽然本应该有两个MethodX,但编译器进行合并处理,对其中一个做跳转,所以我们只需要实现一次就够了。(目前我是这样理解的。)不过这不是本文要探讨的内容,我们关注的是,MethodX、MethodXA、MethodXB又要重复写一遍了。
看来我们要对CXA、CXB也做模版处理:
1template <typename T = IXA>
2struct CXA : public CX<T>
3{
4 virtual void MethodXA()
5 {
6
7 }
8};
9
10template <typename T = IXB>
11struct CXB : public CX<T>
12{
13 virtual void MethodXB()
14 {
15
16 }
17};
18
19struct CXAB : public CXA<IXAB>, public CXB<IXAB>
20{
21 virtual void MethodXAB()
22 {
23
24 }
25};
可惜的是,还是有错误:
error C2259: 'CXAB' : cannot instantiate abstract class
due to following members:
'void IXB::MethodXB(void)' : is abstract
see declaration of 'IXB::MethodXB'
'void IXA::MethodXA(void)' : is abstract
see declaration of 'IXA::MethodXA'
说的是CXA中的MethodXB未实现,CXB中的MethodXA未实现……所以,以上方案不适合接口中存在多继承的情形。
恰好COM中貌似也没有接口多继承的情形。我们将CX改名为IXImpl,将CXA改名为IXAImpl,将CXB改名为IXBImpl:
1struct IX
2{
3 virtual void MethodX() = 0;
4};
5
6struct IXA : public IX
7{
8 virtual void MethodXA() = 0;
9};
10
11struct IXB : public IX
12{
13 virtual void MethodXB() = 0;
14};
15
16template <typename T = IX>
17struct IXImpl : public T
18{
19 virtual void MethodX()
20 {
21
22 }
23};
24
25template <typename T = IXA>
26struct IXAImpl : public IXImpl<T>
27{
28 virtual void MethodXA()
29 {
30
31 }
32};
33
34template <typename T = IXB>
35struct IXBImpl : public IXImpl<T>
36{
37 virtual void MethodXB()
38 {
39
40 }
41};
是不是跟ATL对上号了?这些*Impl类可以直接使用(如果是需要默认实现就够了),也可以被继承,被继承的时候也可以更换基类。如:
1struct CXA : public IXAImpl<>
2{
3
4};
5
6struct IY : public IXB
7{
8 virtual void MethodY() = 0;
9};
10
11struct CY : public IXBImpl<IY>
12{
13 virtual void MethodY()
14 {
15
16 }
17};
第二个例子中,IXB是库提供的接口,IY是应用程序设计的接口,CY是应用程序实现的类。
好了,ATL中的*Impl类的做法就是如此,只是多了其他一些模版参数,暂时不理它们。
首发:http://www.cppblog.com/Streamlet/archive/2012/09/02/189135.html